/****************************************************************************
 * lsu.v
 ****************************************************************************/
`include "defines.v"

/****************************************************************************
   Module: lsu
  
   lsu主要负责数据内存的读写，包含指令：LH、SH、LW、SW。
   lsu读取内存的起始地址为csr寄存器的块选择位与指令立即数合并后的值。
   对于LH、SH等16位的读写单周期即可完成，对于LW、SW等32位的读写需要至少两个周期才能完成，因此lsu需要一些时许的控制。

 ****************************************************************************/

module lsu
(
    input                                   rst,                    // 系统复位信号，同步复位，高电平有效
    input                                   clk,                    // 系统工作时钟

    // 指令译码输入接口
    input   [`REG_WIDTH*2-1 : 0]            op1,                    // 计算操作数1，经过合并处理的结果，imm/reg
    input   [`REG_WIDTH*2-1 : 0]            op2,                    // 计算操作数2，经过合并处理的结果，imm/reg
    input   [`REG_WIDTH-1 : 0]              csr_in,                 // csr寄存器当前的值
    input                                   lsu_en,                 // lsu使能
    input   [`INSTR_INFO_BUS_WIDTH-1 : 0]   instr_info,             // 解析出来的具体的指令信息

    // 结果输出接口
    output  [`REG_WIDTH*2-1 : 0]            reg_data,               // 寄存器结果

    // 数据存储器相关接口，采用icb标准信号接口，exu作为主设备读写mem
    output                                  mem_cmd_wr,             // 写读命令，高电平写，低电平读
    output                                  mem_cmd_req,        
    input                                   mem_cmd_ack,        
    input                                   mem_rsp_req,    
    output                                  mem_rsp_ack,    
    input                                   mem_rsp_err,    
    output  [`DATA_ADDR_WIDTH-1 : 0]        mem_addr,               // 地址
    output  [`DATA_WIDTH-1 : 0]             mem_dout,               // 写数据
    input   [`DATA_WIDTH-1 : 0]             mem_din                 // 读数据
);

    // 指令展开，带门控
    wire lsu_lh = lsu_en & instr_info[`INSTR_INFO_LSU_LH];
    wire lsu_sh = lsu_en & instr_info[`INSTR_INFO_LSU_SH];
    wire lsu_lw = lsu_en & instr_info[`INSTR_INFO_LSU_LW];
    wire lsu_sw = lsu_en & instr_info[`INSTR_INFO_LSU_SW];

    // 内存输入接口
    wire    mem_sel  = csr_in[8 : 6];
    assign  mem_addr = {mem_sel, op2[7:0]};
    assign  mem_dout = op1[`REG_WIDTH-1 : 0];
    assign  mem_cmd_wr = lsu_en & (lsu_lh & (~lsu_sh));

    // 内存输出接口
    assign  reg_data = mem_din;

endmodule
